Skip to contentMethod: createNewInstance(Class, int)
1: /*
2: * JOPA
3: * Copyright (C) 2023 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.sessions;
19:
20: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
21: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
22: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
23:
24: import java.lang.reflect.Constructor;
25: import java.lang.reflect.Field;
26: import java.lang.reflect.InvocationTargetException;
27: import java.security.AccessController;
28: import java.security.PrivilegedActionException;
29: import java.util.Collection;
30: import java.util.Collections;
31: import java.util.HashMap;
32: import java.util.Map;
33: import java.util.Map.Entry;
34:
35: class MapInstanceBuilder extends AbstractInstanceBuilder {
36:
37: private static final Class<?> singletonMapClass = Collections.singletonMap(null, null)
38: .getClass();
39:
40: MapInstanceBuilder(CloneBuilderImpl builder, UnitOfWorkImpl uow) {
41: super(builder, uow);
42: }
43:
44: @Override
45: Object buildClone(Object cloneOwner, Field field, Object original, CloneConfiguration configuration) {
46: Map<?, ?> orig = (Map<?, ?>) original;
47: if (original instanceof IndirectCollection) {
48: orig = ((IndirectCollection<Map<?, ?>>) original).unwrap();
49: }
50: if (orig == Collections.emptyMap()) {
51: return orig;
52: }
53: final Class<?> origCls = orig.getClass();
54: Map<?, ?> clone;
55: clone = cloneUsingDefaultConstructor(cloneOwner, field, origCls, orig, configuration);
56: if (clone == null) {
57: if (singletonMapClass.isInstance(orig)) {
58: clone = buildSingletonClone(cloneOwner, field, orig, configuration);
59: } else if (Collections.emptyMap().equals(orig)) {
60: clone = orig;
61: }
62: else {
63: throw new IllegalArgumentException("Unsupported map type " + origCls);
64: }
65: }
66: clone = (Map<?, ?>) uow.createIndirectCollection(clone, cloneOwner, field);
67: return clone;
68:
69: }
70:
71: private Map<?, ?> cloneUsingDefaultConstructor(Object cloneOwner, Field field, Class<?> origCls, Map<?, ?> original,
72: CloneConfiguration configuration) {
73: Map<?, ?> result = createNewInstance(origCls, original.size());
74: if (result != null) {
75: cloneMapContent(cloneOwner, field, original, result, configuration);
76: }
77: return result;
78: }
79:
80: private static Map<?, ?> createNewInstance(Class<?> type, int size) {
81: Map<?, ?> result = null;
82: final Class<?>[] types = {int.class};
83: Object[] params;
84: Constructor<?> c = getDeclaredConstructorFor(type, types);
85:• if (c != null) {
86: params = new Object[1];
87: params[0] = size;
88: } else {
89: c = getDeclaredConstructorFor(type, null);
90: params = null;
91: }
92:• if (c == null) {
93: return null;
94: }
95: try {
96: result = (Map<?, ?>) c.newInstance(params);
97: } catch (InstantiationException | IllegalArgumentException | InvocationTargetException e) {
98: throw new OWLPersistenceException(e);
99: } catch (IllegalAccessException e) {
100: logConstructorAccessException(c, e);
101: try {
102: result = (Map<?, ?>) AccessController
103: .doPrivileged(new PrivilegedInstanceCreator(c));
104: } catch (PrivilegedActionException ex) {
105: logPrivilegedConstructorAccessException(c, ex);
106: // Do nothing
107: }
108: }
109: return result;
110: }
111:
112: private Map<?, ?> buildSingletonClone(Object cloneOwner, Field field, Map<?, ?> orig,
113: CloneConfiguration configuration) {
114: Entry<?, ?> e = orig.entrySet().iterator().next();
115: Object key = CloneBuilderImpl.isImmutable(e.getKey()) ? e.getKey() :
116: cloneObject(cloneOwner, field, e.getKey(), configuration);
117: Object value = CloneBuilderImpl.isImmutable(e.getValue()) ? e.getValue() :
118: cloneObject(cloneOwner, field, e.getValue(), configuration);
119: if ((value instanceof Collection || value instanceof Map) && !(value instanceof IndirectCollection)) {
120: value = uow.createIndirectCollection(value, cloneOwner, field);
121: }
122: return Collections.singletonMap(key, value);
123: }
124:
125: private void cloneMapContent(Object cloneOwner, Field field, Map<?, ?> source,
126: Map<?, ?> target, CloneConfiguration configuration) {
127: if (source.isEmpty()) {
128: return;
129: }
130: final Map<Object, Object> m = (Map<Object, Object>) target;
131: final Entry<?, ?> tmp = source.entrySet().iterator().next();
132: // Note: If we encounter null -> null mapping first, the whole map will be treated as immutable type map, which can be incorrect
133: final boolean keyPrimitive = CloneBuilderImpl.isImmutable(tmp.getKey());
134: final boolean valuePrimitive = CloneBuilderImpl.isImmutable(tmp.getValue());
135: for (Entry<?, ?> e : source.entrySet()) {
136: Object key;
137: Object value;
138: if (keyPrimitive) {
139: if (valuePrimitive) {
140: m.putAll(source);
141: break;
142: }
143: key = e.getKey();
144: value = cloneObject(cloneOwner, field, e.getValue(), configuration);
145: } else {
146: key = cloneObject(cloneOwner, field, e.getKey(), configuration);
147: value = valuePrimitive ? e.getValue() : cloneObject(cloneOwner, field, e.getValue(), configuration);
148: }
149: m.put(key, value);
150: }
151: }
152:
153: private Object cloneObject(Object owner, Field field, Object obj, CloneConfiguration configuration) {
154: Object clone;
155: if (obj == null) {
156: clone = null;
157: } else if (builder.isTypeManaged(obj.getClass())) {
158: clone = uow.registerExistingObject(obj, configuration.getDescriptor(), configuration.getPostRegister());
159: } else {
160: clone = builder.buildClone(owner, field, obj, configuration.getDescriptor());
161: }
162: return clone;
163: }
164:
165: @Override
166: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
167: assert (originalValue == null) || (originalValue instanceof Map);
168: assert cloneValue instanceof Map;
169:
170: Map<Object, Object> orig = (Map<Object, Object>) originalValue;
171: Map<Object, Object> clone = (Map<Object, Object>) cloneValue;
172: if (clone instanceof IndirectCollection) {
173: clone = ((IndirectCollection<Map<Object, Object>>) clone).unwrap();
174: }
175: if (orig == null) {
176: orig = (Map<Object, Object>) createNewInstance(clone.getClass(), clone.size());
177: if (orig == null) {
178: orig = createDefaultMap(clone.size());
179: }
180: EntityPropertiesUtils.setFieldValue(field, target, orig);
181: }
182: orig.clear();
183: if (clone.isEmpty()) {
184: return;
185: }
186: for (Entry<?, ?> e : clone.entrySet()) {
187: final Object key = e.getKey();
188: final Object value = e.getValue();
189: final Object keyToPut = uow.contains(key) ? builder.getOriginal(key) : key;
190: final Object valueToPut = uow.contains(value) ? builder.getOriginal(value) : value;
191: orig.put(keyToPut, valueToPut);
192: }
193: }
194:
195: private static Map<Object, Object> createDefaultMap(int size) {
196: return new HashMap<>(size > 1 ? size : 16);
197: }
198:
199: @Override
200: boolean populatesAttributes() {
201: return true;
202: }
203: }